package com.wei.service.impl;

import com.wei.common.ApplicationCacheDefinition;
import com.wei.common.SysConfigCodeDefinition;
import com.wei.dao.Page;
import com.wei.dao.read.PatientReadMapper;
import com.wei.dao.read.PtRelationReadMapper;
import com.wei.dao.read.SysConfigReadMapper;
import com.wei.dao.read.TokenReadMapper;
import com.wei.dao.write.PatientOptMapper;
import com.wei.dao.write.PtRelationOptMapper;
import com.wei.dao.write.SysConfigOptMapper;
import com.wei.dao.write.TokenOptMapper;
import com.wei.entity.Patient;
import com.wei.entity.PtRelation;
import com.wei.entity.SysConfig;
import com.wei.entity.Token;
import com.wei.entity.dto.PatientDto;
import com.wei.entity.vo.PatientVo;
import com.wei.service.IPatientService;
import com.wei.service.IWechatAuthService;
import com.wei.service.mapstruct.PatientMapperImpl;
import com.wei.service.wrapper.HttpContextWrapper;
import com.wei.service.wrapper.Locker;
import com.wei.service.wrapper.RedisCacheWrapper;
import com.wei.utils.ApplicationConstants;
import com.wei.utils.ExceptionUtils;
import com.wei.utils.PinyinUtils;
import com.wei.utils.Stringutils;
import com.wei.utils.uuid.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.*;

@Service
@Transactional
public class PatientServiceImpl implements IPatientService {
    @Autowired
    private RedisCacheWrapper redisCacheWrapper;
    @Autowired
    private HttpContextWrapper httpContextWrapper;
    //@Autowired
    //private Locker locker;
    @Autowired
    private PatientOptMapper patientOptMapper;
    @Autowired
    private PatientReadMapper patientReadMapper;
    @Autowired
    private SysConfigReadMapper sysConfigReadMapper;
    @Autowired
    private SysConfigOptMapper sysConfigOptMapper;
    @Autowired
    private TokenReadMapper tokenReadMapper;
    @Autowired
    private TokenOptMapper tokenOptMapper;
    @Autowired
    private PtRelationOptMapper ptRelationOptMapper;
    @Autowired
    private PtRelationReadMapper ptRelationReadMapper;
    @Autowired
    private IWechatAuthService wechatAuthService;
    /**
     * 需要手动控制事务
     */
    @Autowired
    private DataSourceTransactionManager transactionManager;

    private final static Object lock = new Object();

    public boolean insert(Patient patient) {
        return patientOptMapper.insert(patient) > 0;
    }

    public boolean update(Patient patient) {
        return patientOptMapper.update(patient) > 0;
    }

    public boolean delete(Integer id) {
        return patientOptMapper.delete(id) > 0;
    }

    public boolean batchSava(List<Patient> patientList) {
        return patientOptMapper.batchSava(patientList) > 0;
    }

    public Patient getById(Integer id) {
        return patientReadMapper.getById(id);
    }

    public Integer totalCount() {
        return patientReadMapper.totalCount();
    }

    public List<Patient> getAll(Page<Patient> page) {
        return patientReadMapper.getAll(page);
    }

    public PatientDto createPatient(PatientDto patientDto) {
        //TODO 重复提交问题
        //手机验证码校验
        if(Stringutils.isEmpty(patientDto.getPhone())){
            throw ExceptionUtils.mpeValidator("%s", "请输入手机号!");
        }
        if(Stringutils.isEmpty(patientDto.getCheckNum())){
            throw ExceptionUtils.mpeValidator("%s", "请输入校验码!");
        }
        if(!wechatAuthService.verifyCode(patientDto.getPhone(),patientDto.getCheckNum())){
            throw ExceptionUtils.mpeValidator("%s", "请先获取校验码!");
        }
        //TODO 身份证号码和姓名校验 必须匹配
        Patient patient = PatientMapperImpl.INSTANCE.patientDtoToPatient(patientDto);
        //TODO 自动校验validator自定义实现
        if (patient == null) {//转换失败
            throw ExceptionUtils.mpeValidator("%s", "patient转换失败");
        } else if (Stringutils.isEmpty(patient.getPtCardid()) && Stringutils.isEmpty(patient.getPtGuardianCardno())) {//身份证号不能为空
            throw ExceptionUtils.mpeValidator("%s", "身份证号不能为空");
        }
        Patient check = patientReadMapper.getByCardno(patient.getPtCardid());
        if (check != null) {//该身份证号已经建档
            throw ExceptionUtils.mpeValidator("%s", "身份证信息已经建档");
        }
        //设置拼音
        patient.setPtPinyin(PinyinUtils.convertHanzi2Pinyin(patient.getPtName(), false, ""));
        //TODO 设置省市县等信息

        // lock会一直等待获取到锁
        //locker.lock(SysConfigCodeDefinition.CODE_100011);
        String uuid = UUID.fastUUID().toString(true);
        redisCacheWrapper.waitLock(SysConfigCodeDefinition.CODE_100012, uuid, 10);
        //获取诊疗卡号
        SysConfig sysConfig = sysConfigReadMapper.getBySysCode(SysConfigCodeDefinition.CODE_100011);
        if (sysConfigOptMapper.increment(sysConfig) <= 0) {
            throw ExceptionUtils.mpe("%s", "更新诊疗卡号失败");
        }
        patientDto.setIoNo(sysConfig.getSysNumber().toString());
        patient.setPtIoNo(sysConfig.getSysNumber().toString());
        //释放锁
        //locker.unlock(SysConfigCodeDefinition.CODE_100011);
        redisCacheWrapper.unLock(SysConfigCodeDefinition.CODE_100012, uuid);
        //没有获取到诊疗卡号的话
        if (Stringutils.isEmpty(patient.getPtIoNo())) {
            throw ExceptionUtils.mpeValidator("%s", "诊疗卡号获取失败");
        }
        //保存病人信息
        patient.setPtCreateDt(new Date());
        //TODO 创建人 是否存在全局配置
        patient.setPtCreateCode("000");
        insert(patient);
        //保存token
        String tk = httpContextWrapper.getToken();
        Token token = tokenReadMapper.getById(tk);
        if (token == null) {
            token = new Token();
            token.setToken(tk);
            token.setId(patient.getPtId());
            token.setModDt(new Date());
            token.setType("01");
            tokenOptMapper.insert(token);
        }
        //1.判断当前token是否存在病人信息
        Patient currentPatient = httpContextWrapper.CurrentPatient();
        if (currentPatient != null && currentPatient.getPtId() > 0) {
            //2.把所有其他当前患者更新成N
            ptRelationOptMapper.updateCrtFlagByPtid(currentPatient.getPtId());
            //3.保存关系
            PtRelation ptRelation = new PtRelation();
            ptRelation.setRelationCardNo(currentPatient.getPtIoNo());
            ptRelation.setRelationPtId(currentPatient.getPtId());
            ptRelation.setRelationCardRelationNo(patient.getPtIoNo());
            ptRelation.setRelationPtRelationId(patient.getPtId());
            ptRelation.setRelationCrtFlag(ApplicationConstants.Y);
            ptRelation.setRelationFlag(ApplicationConstants.Y);
            ptRelation.setRelationModDt(new Date());
            ptRelationOptMapper.insert(ptRelation);
        }
        //清空缓存
        redisCacheWrapper.delete(Stringutils.format(ApplicationCacheDefinition.PATIENTLIST, HttpContextWrapper.class.toString(), tk));
        return patientDto;
    }


    public List<PatientDto> getBindPatientDtoList() {
        List<PatientVo> patientList = httpContextWrapper.BindPatientList();
        return PatientMapperImpl.INSTANCE.patientVoListToPatientDtoList(patientList);
    }

    /**
     * 设置当前就诊人
     *
     * @param ioNo 门诊号
     * @return
     */
    public List<PatientDto> fitCrtPatient(String ioNo) {
        if (Stringutils.isEmptyOrWhiteSpace(ioNo)) {
            throw ExceptionUtils.mpeValidator("ioNo参数不能为空!");
        }
        Patient currentPatient = httpContextWrapper.CurrentPatient();

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);

        try {
            if (currentPatient != null && currentPatient.getPtId() > 0) {
                ptRelationOptMapper.updateCrtFlagByPtid(currentPatient.getPtId());
            }
            if (!ioNo.equals(currentPatient.getPtIoNo())) {
                Map<String, String> map = new HashMap<>();
                map.put("crtFlag", ApplicationConstants.Y);
                map.put("ioNo", ioNo);
                ptRelationOptMapper.updateCrtFlagByIoNo(map);
            }
            transactionManager.commit(status);
        } catch (Exception ex) {
            transactionManager.rollback(status);
            throw ExceptionUtils.mpe(ex);
        }
        String tk = httpContextWrapper.getToken();
        //清空缓存
        redisCacheWrapper.delete(Stringutils.format(ApplicationCacheDefinition.PATIENTLIST, HttpContextWrapper.class.toString(), tk));
        return getBindPatientDtoList();
    }

    /**
     * 解绑
     *
     * @param ioNo 门诊号
     * @return
     */
    public List<PatientDto> unBindPatient(String ioNo) {
        //判断该门诊号是否是否是绑定主患者信息
        if (Stringutils.isEmptyOrWhiteSpace(ioNo)) {
            throw ExceptionUtils.mpeValidator("ioNo参数不能为空!");
        }
        Patient currentPatient = httpContextWrapper.CurrentPatient();
        if (ioNo.equals(currentPatient.getPtIoNo())) {
            throw ExceptionUtils.mpeValidator("绑定主信息无法解绑!");
        }
        Patient relationPatient = patientReadMapper.getByIoNo(ioNo);
        if(relationPatient==null){
            throw ExceptionUtils.mpeValidator("该门诊号不存在!");
        }

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            Map<String, String> map = new HashMap<>();
            map.put("crtFlag", ApplicationConstants.N);
            map.put("relationId", relationPatient.getPtId().toString());
            map.put("id", currentPatient.getPtId().toString());
            ptRelationOptMapper.unBindPatient(map);

            transactionManager.commit(status);
        } catch (Exception ex) {
            transactionManager.rollback(status);
            throw ExceptionUtils.mpe(ex);
        }
        String tk = httpContextWrapper.getToken();
        //清空缓存
        redisCacheWrapper.delete(Stringutils.format(ApplicationCacheDefinition.PATIENTLIST, HttpContextWrapper.class.toString(), tk));
        return getBindPatientDtoList();

    }

    /**
     * 绑卡
     *
     * @param patientDto
     * @return
     */
    public List<PatientDto> bindPatient(PatientDto patientDto) {
        if(Stringutils.isEmpty(patientDto.getPhone())){
            throw ExceptionUtils.mpeValidator("%s", "请输入手机号!");
        }
        if(Stringutils.isEmpty(patientDto.getCheckNum())){
            throw ExceptionUtils.mpeValidator("%s", "请输入校验码!");
        }
        if(!wechatAuthService.verifyCode(patientDto.getPhone(),patientDto.getCheckNum())){
            throw ExceptionUtils.mpeValidator("%s", "请先获取校验码!");
        }
        //验证信息是否在病人信息表存在
        Patient patient = PatientMapperImpl.INSTANCE.patientDtoToPatient(patientDto);
        if (patient == null) {//转换失败
            throw ExceptionUtils.mpeValidator("%s", "patient转换失败");
        } else if (Stringutils.isEmpty(patient.getPtCardid()) && Stringutils.isEmpty(patient.getPtGuardianCardno())) {//身份证号不能为空
            throw ExceptionUtils.mpeValidator("%s", "身份证号不能为空");
        }
        if (!Stringutils.isEmpty(patient.getPtCardid())) {
            //验证身份证号码
            patient = patientReadMapper.getByCardno(patient.getPtCardid());

        } else {
            //验证监护人身份证号码
            patient = patientReadMapper.getByGuardCardno(patient.getPtGuardianCardno());
        }
        if (patient == null) {
            throw ExceptionUtils.mpeValidator("%s", "身份证号码信息不存在,请先建档!");
        }
        //验证手机号码
        if (!patient.getPtPhone().equals(patientDto.getPhone())) {
            throw ExceptionUtils.mpeValidator("%s", "号码与医院预留号码不一致,请核对!");
        }
        //验证姓名
        if(!patient.getPtName().equals(patientDto.getName())){
            throw ExceptionUtils.mpeValidator("%s", "姓名与医院预留号码不一致,请核对!");
        }

        //保存token
        String tk = httpContextWrapper.getToken();
        Patient currentPatient = httpContextWrapper.CurrentPatient();

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            Token token = tokenReadMapper.getById(tk);
            if (token == null) {
                token = new Token();
                token.setToken(tk);
                token.setId(patient.getPtId());
                token.setModDt(new Date());
                token.setType("01");
                tokenOptMapper.insert(token);
            }
            if (currentPatient != null && currentPatient.getPtId() > 0) {
                ptRelationOptMapper.updateCrtFlagByPtid(currentPatient.getPtId());
            }

            if (currentPatient != null && currentPatient.getPtId() > 0) {
                Map<String, String> map = new HashMap<>();
                map.put("relationId", patient.getPtId().toString());
                map.put("id", currentPatient.getPtId().toString());
                PtRelation relation = ptRelationReadMapper.getByIdAndRelationId(map);
                if (relation != null && relation.getRelationPtId() > 0) {
                    if (ApplicationConstants.Y.equals(relation.getRelationFlag())) {
                        throw ExceptionUtils.mpeValidator("%s", "已经绑定该病人信息,请勿重复绑定!");
                    } else {
                        Map<String, String> updateMap = new HashMap<>();
                        updateMap.put("crtFlag", ApplicationConstants.Y);
                        updateMap.put("relationId", relation.getRelationPtRelationId().toString());
                        updateMap.put("id", relation.getRelationPtId().toString());
                        ptRelationOptMapper.unBindPatient(updateMap);
                    }
                } else {
                    PtRelation ptRelation = new PtRelation();
                    ptRelation.setRelationCardNo(currentPatient.getPtIoNo());
                    ptRelation.setRelationPtId(currentPatient.getPtId());
                    ptRelation.setRelationCardRelationNo(patient.getPtIoNo());
                    ptRelation.setRelationPtRelationId(patient.getPtId());
                    ptRelation.setRelationCrtFlag(ApplicationConstants.Y);
                    ptRelation.setRelationFlag(ApplicationConstants.Y);
                    ptRelation.setRelationModDt(new Date());
                    ptRelationOptMapper.insert(ptRelation);
                }
            }
            transactionManager.commit(status);
        } catch (Exception ex) {
            transactionManager.rollback(status);
            throw ExceptionUtils.mpe(ex);
        }
        //清空缓存
        redisCacheWrapper.delete(Stringutils.format(ApplicationCacheDefinition.PATIENTLIST, HttpContextWrapper.class.toString(), tk));
        return getBindPatientDtoList();
    }

    /**
     * 获取就诊卡号
     * @param map
     * @return
     */
    public String getIoNo(Map<String,String> map){
        String isChild = map.get("child");
        String cardNo = map.get("cardNo");
        if(Stringutils.isEmpty(isChild)){
            throw ExceptionUtils.mpeValidator("%s", "患者类型不能为空!");
        }
        if(Stringutils.isEmpty(cardNo)){
            throw ExceptionUtils.mpeValidator("%s", "患者卡号不能为空!");
        }
        Patient patient = null;
        if(ApplicationConstants.Y.equals(isChild)){
            patient = patientReadMapper.getByGuardCardno(cardNo);
        }else{
            patient = patientReadMapper.getByCardno(cardNo);
        }
        if(patient==null){
            throw ExceptionUtils.mpeValidator("%s", "患者信息在本院不存在,请先建档!");
        }
        return patient.getPtIoNo();
    }
}
